﻿using Dapper;
using log4net;
using Npgsql;
using System.Data;

namespace DAL.DataProcess;

public class DataAccessImpl : IDataAccess
{
    private string _connectionStr;
    private IDbConnection _dbConnection;
    private readonly ILog _log = LogManager.GetLogger(typeof(DataAccessImpl));

    public smErrorCode Initialize(string connectionStr)
    {
        if (string.IsNullOrEmpty(connectionStr))
        {
            _log.Error($"[Initialize][数据库连接字符串为空]");
            throw new ArgumentNullException("数据库连接字符串为空");
        }

        _connectionStr = connectionStr;

        //托管给Dapper管理连接
        IDbConnection connection = new NpgsqlConnection(_connectionStr);
        _dbConnection = connection;

        return smErrorCode.sm_ec_Success;
    }

    public smErrorCode Start()
    {
        return smErrorCode.sm_ec_Success;
    }

    public smErrorCode Stop()
    {
        return smErrorCode.sm_ec_Success;
    }

    public smErrorCode Connect()
    {
        if (string.IsNullOrEmpty(_connectionStr))
        {
            _log.Error($"[Connect][数据库连接字符串为空]");
            throw new ArgumentNullException("数据库连接字符串为空");
        }
        var connection = new NpgsqlConnection(_connectionStr);

        if (connection.State == ConnectionState.Closed)
        {
            try
            {
                connection.Open();
                _dbConnection = connection;
            }
            catch (Exception ex)
            {
                _log.Error($"[Connect][数据库连接异常]");
                throw new CustomException($"[数据库连接异常]", ex, smErrorCode.dt_ec_ConnectFaild);
            }
        }
        else
        {
            _dbConnection = connection;
        }

        return smErrorCode.sm_ec_Success;
    }

    #region IDisposable Support

    private bool disposedValue = false; // 要检测冗余调用

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            if (disposing)
            {
                // TODO: 释放托管状态(托管对象)。
                _dbConnection?.Dispose();
            }

            // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
            // TODO: 将大型字段设置为 null。

            disposedValue = true;
        }
    }

    // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
    //        ~smDataAccess() {
    //          // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
    //          Dispose(false);
    //        }

    // 添加此代码以正确实现可处置模式。
    public void Dispose()
    {
        // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //Logger.log(LogLevel.Info, $"[smDataAccess.Dispose]释放数据库连接资源");
        Dispose(true);
        // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
        // GC.SuppressFinalize(this);
    }

    #endregion IDisposable Support

    #region 同步

    public T Get<T, TKey>(TKey id)
    {
        T res = _dbConnection.Get<T>(id);
        return res;
    }

    public T Get<T>(string conditions, object parameters = null) => _dbConnection.QueryFirstOrDefault<T>(conditions, parameters);

    public IEnumerable<T> GetList<T>() => _dbConnection.GetList<T>();

    public IEnumerable<T> GetList<T>(object whereConditions) where T : class => DapperExtensions.DapperExtensions.GetList<T>(_dbConnection, whereConditions);

    public IEnumerable<T> GetList<T>(string conditions, object parameters = null) => _dbConnection.GetList<T>(conditions, parameters);

    public IEnumerable<T> GetListPaged<T>(int pageNumber, int rowsPerPage, string conditions, string orderBy, object parameters = null)
    {
        return _dbConnection.GetListPaged<T>(pageNumber, rowsPerPage, conditions, orderBy, parameters);
    }

    public int? Insert<T>(T entity) where T : class => DapperExtensions.DapperExtensions.Insert(_dbConnection, entity);

    public int? InsertOri<T>(T entity) where T : class
    {
        return _dbConnection.Insert(entity);
    }

    public TKey Insert<TKey, T>(TKey id, T entity)
    {
        return _dbConnection.Insert<TKey, T>(entity);
    }

    public void BatchInsert<T>(IEnumerable<T> entities, IDbTransaction transaction = null) where T : class => DapperExtensions.DapperExtensions.Insert(_dbConnection, entities, transaction);

    public bool Update<T>(T entity) where T : class => DapperExtensions.DapperExtensions.Update(_dbConnection, entity);

    public int Delete<T>(T entity) => _dbConnection.Delete(entity);

    public bool DeleteList<T>(object whereConditions, IDbTransaction transaction = null, int? commandTimeout = null)
        where T : class
        => DapperExtensions.DapperExtensions.Delete<T>(_dbConnection, whereConditions, transaction, commandTimeout);

    public int DeleteList<T>(string conditions, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null)
    {
        return _dbConnection.DeleteList<T>(conditions, parameters, transaction, commandTimeout);
    }

    public int RecordCount<T>(string conditions = "", object parameters = null)
    {
        return _dbConnection.RecordCount<T>(conditions, parameters);
    }

    #endregion 同步

    #region 异步

    public Task<T> GetAsync<T, TKey>(TKey id)
    {
        return _dbConnection.GetAsync<T>(id);
    }

    public Task<T> GetAsync<T>(string conditions, object parameters = null) => _dbConnection.QueryFirstOrDefaultAsync<T>(conditions, parameters);

    public Task<IEnumerable<T>> GetListAsync<T>()
    {
        return _dbConnection.GetListAsync<T>();
    }

    public Task<IEnumerable<T>> GetListAsync<T>(object whereConditions)
    {
        return _dbConnection.GetListAsync<T>(whereConditions);
    }

    public Task<IEnumerable<T>> GetListAsync<T>(string conditions, object parameters = null)
    {
        return _dbConnection.GetListAsync<T>(conditions, parameters);
    }

    public Task<IEnumerable<T>> GetListPagedAsync<T>(int pageNumber, int rowsPerPage, string conditions, string orderby, object parameters = null)
    {
        return _dbConnection.GetListPagedAsync<T>(pageNumber, rowsPerPage, conditions, orderby, parameters);
    }

    public Task<int?> InsertAsync<T>(T entity)
    {
        return _dbConnection.InsertAsync(entity);
    }

    public Task<int> UpdateAsync<T>(T entity)
    {
        return _dbConnection.UpdateAsync(entity);
    }

    public Task<int> DeleteAsync<T>(T entity)
    {
        return _dbConnection.DeleteAsync(entity);
    }

    public Task<int> DeleteListAsync<T>(object whereConditions, IDbTransaction transaction = null, int? commandTimeout = null)
    {
        return _dbConnection.DeleteListAsync<T>(whereConditions, transaction, commandTimeout);
    }

    public Task<int> DeleteListAsync<T>(string conditions, object parameters = null, IDbTransaction transaction = null, int? commandTimeout = null)
    {
        return _dbConnection.DeleteListAsync<T>(conditions, parameters, transaction, commandTimeout);
    }

    public Task<int> RecordCountAsync<T>(string conditions = "", object parameters = null)
    {
        return _dbConnection.RecordCountAsync<T>(conditions, parameters);
    }

    #endregion 异步

    #region 同步--原始SQL

    public IEnumerable<T> Query<T>(string sql, object param = null, IDbTransaction transaction = null, bool buffered = true,
        int? commandTimeout = null, CommandType? commandType = null)
    {
        IEnumerable<T> res = _dbConnection.Query<T>(sql, param, transaction, buffered, commandTimeout, commandType);
        return res;
    }

    public IEnumerable<TReturn> Query<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        IEnumerable<TReturn> res = _dbConnection.Query(sql, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);
        return res;
    }

    public IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TReturn>(string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        IEnumerable<TReturn> res = _dbConnection.Query(sql, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);

        return res;
    }

    public IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        IEnumerable<TReturn> res = _dbConnection.Query(sql, map, param, transaction, buffered,
               splitOn, commandTimeout, commandType);

        return res;
    }

    public IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        IEnumerable<TReturn> res = _dbConnection.Query(sql, map, param, transaction, buffered,
               splitOn, commandTimeout, commandType);

        return res;
    }

    public IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(string sql,
        Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        IEnumerable<TReturn> res = _dbConnection.Query(
                sql, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);

        return res;
    }

    public IEnumerable<TReturn> Query<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(string sql,
        Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        IEnumerable<TReturn> res = _dbConnection.Query(
                sql, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);

        return res;
    }

    public IEnumerable<TReturn> Query<TReturn>(string sql, Type[] types, Func<object[], TReturn> map, object param = null, IDbTransaction transaction = null,
        bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
    {
        IEnumerable<TReturn> res = _dbConnection.Query(
                 sql, types, map, param, transaction, buffered,
                 splitOn, commandTimeout, commandType);

        return res;
    }

    public int Execute(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
        CommandType? commandType = null)
    {
        int res = _dbConnection.Execute(sql, param, transaction, commandTimeout, commandType);

        return res;
    }

    #endregion 同步--原始SQL

    #region 异步--原始SQL

    public Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, bool buffered = true,
        int? commandTimeout = null, CommandType? commandType = null)
    {
        Task<IEnumerable<T>> res = _dbConnection.QueryAsync<T>(sql, param, transaction, commandTimeout, commandType);

        return res;
    }

    public Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TReturn>(string sql, Func<TFirst, TSecond, TReturn> map, object param = null, IDbTransaction transaction = null,
        bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
    {
        Task<IEnumerable<TReturn>> res = _dbConnection.QueryAsync(sql, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);

        return res;
    }

    public Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TReturn>(string sql, Func<TFirst, TSecond, TThird, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        Task<IEnumerable<TReturn>> res = _dbConnection.QueryAsync(sql, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);

        return res;
    }

    public Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        Task<IEnumerable<TReturn>> res = _dbConnection.QueryAsync(sql, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);

        return res;
    }

    public Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TReturn>(string sql, Func<TFirst, TSecond, TThird, TFourth, TFifth, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        Task<IEnumerable<TReturn>> res = _dbConnection.QueryAsync(
                 sql, map, param, transaction, buffered,
                 splitOn, commandTimeout, commandType);

        return res;
    }

    public Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn>(string sql,
        Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TReturn> map, object param = null,
        IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id", int? commandTimeout = null,
        CommandType? commandType = null)
    {
        Task<IEnumerable<TReturn>> res = _dbConnection.QueryAsync(
                 sql, map, param, transaction, buffered,
                 splitOn, commandTimeout, commandType);

        return res;
    }

    public Task<IEnumerable<TReturn>> QueryAsync<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn>(string sql,
        Func<TFirst, TSecond, TThird, TFourth, TFifth, TSixth, TSeventh, TReturn> map,
        object param = null, IDbTransaction transaction = null, bool buffered = true, string splitOn = "Id",
        int? commandTimeout = null, CommandType? commandType = null)
    {
        Task<IEnumerable<TReturn>> res = _dbConnection.QueryAsync(
                sql, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);

        return res;
    }

    public Task<IEnumerable<TReturn>> QueryAsync<TReturn>(string sql, Type[] types, Func<object[], TReturn> map, object param = null, IDbTransaction transaction = null,
        bool buffered = true, string splitOn = "Id", int? commandTimeout = null, CommandType? commandType = null)
    {
        Task<IEnumerable<TReturn>> res = _dbConnection.QueryAsync(
                sql, types, map, param, transaction, buffered,
                splitOn, commandTimeout, commandType);

        return res;
    }

    public Task<int> ExecuteAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null,
        CommandType? commandType = null)
    {
        Task<int> res = _dbConnection.ExecuteAsync(sql, param, transaction, commandTimeout, commandType);

        return res;
    }

    #endregion 异步--原始SQL

    #region 事务

    public IDbTransaction GetTransaction()
    {
        Connect(); //Dapper托管情况下需要手动开启数据库连接
        return _dbConnection.BeginTransaction();
    }

    #endregion 事务
}